local pb = require "pb"
local OnePB = require "OnePB"

Lib.Protos = Lib.Protos or {}

local Buffer = {
}
Lib.Buffer = Buffer

function Buffer:Init(data)
	self.pos = 0
	self.data = data
	self.limit = #data
end

function Buffer:ReadTag()
	if self.pos >= self.limit then
		return 0
	end

	local tag, new_pos = pb.read_fieldId(self.data, self.pos)
	--print("Buffer:", tag, new_pos)
	self.pos = new_pos
	return tag
end
function Buffer:ReadTagAndType(  )
	if self.pos >= self.limit then
		return 0
	end
	local tag, ty, new_pos = pb.read_fIdtype(self.data, self.pos)
	self.pos = new_pos
	return tag, ty
end

--跳过一个Block数
function Buffer:DefaultRead( tag, ty )
	if ty == 0 then
		self:ReadInt()
	elseif ty == 2 then
		self:ReadString()
	else
		LogErr("UnknownType", self.pos, tag, ty)
	end
end

function Buffer:ReadInt()
	local val, new_pos = pb.signed_varint_decoder(self.data, self.pos)
	self.pos = new_pos
	--print("ReadInt", val, new_pos)
	return val
end

function Buffer:ReadUnSignedInt(  )
	local val, new_pos = pb.signed_varint_decoder(self.data, self.pos)
	self.pos = new_pos
	return val
end

--bool也只是个Int
function Buffer:ReadBool(  )
	local val, new_pos = pb.signed_varint_decoder(self.data, self.pos)
	self.pos = new_pos
	return val == 1
end


function Buffer:ReadString()
	local val, new_pos = pb.signed_varint_decoder(self.data, self.pos)
	local stPos = new_pos
	local endPos = new_pos+val

	self.pos = endPos
	--print("ReadStr", stPos, endPos, val)
	return string.sub(self.data, stPos+1, endPos)
end


function Buffer:ReadMsg(msgObj)
	local val, new_pos = pb.signed_varint_decoder(self.data, self.pos)
	local stPos = new_pos
	local endPos = new_pos+val
	self.pos = stPos
	local oldLimit = self.limit
	self.limit = endPos
	msgObj:Parse(self)
	self.limit = oldLimit
	self.pos = endPos
	return msgObj
end


local WriteBuffer = {
}

Lib.WriteBuffer = WriteBuffer

function WriteBuffer:Init(shareBuff)
	-- self.fields = {}
	if shareBuff == nil then
		shareBuff = OnePB.openPack()
	end
	self.shareBuff = shareBuff
end

function WriteBuffer:WriteInt(fieldId, val)
	local tag = fieldId * 8 + 0
	OnePB.int_encoder(self.shareBuff, tag)
	OnePB.int_encoder(self.shareBuff, val)

	-- local v = pb.int_encoder(tag)
	-- table.insert(self.fields, v)
	-- -- Log("WriteInt", fieldId, val, type(val))
	-- local v = pb.int_encoder(val)
	-- table.insert(self.fields, v)	
end

function WriteBuffer:WriteUnSignedInt( fieldId, val )
	local tag = fieldId * 8 + 0
	OnePB.int_encoder(self.shareBuff, tag)
	OnePB.int_encoder(self.shareBuff, val)

	-- local v = pb.int_encoder(tag)
	-- table.insert(self.fields, v)
	-- local v = pb.int_encoder(val)
	-- table.insert(self.fields, v)	
end

function WriteBuffer:WriteBool( fieldId, val )
	local tag = fieldId * 8 + 0
	OnePB.int_encoder(self.shareBuff, tag)
	local encodeV = 0
	if val then encodeV = 1 end
	OnePB.int_encoder(self.shareBuff, encodeV)

	-- local v = pb.int_encoder(tag)
	-- table.insert(self.fields, v)

	-- local encodeV = 0
	-- if val then encodeV = 1 end
	-- local v = pb.int_encoder(encodeV)
	-- table.insert(self.fields, v)
end


function WriteBuffer:WriteString(fieldId, val)
	--写入字符串
	-- if type(val) == "userdata" then
	-- 	val = OnePB.getString(val)
	-- end

	local tag = fieldId * 8 + 2
	OnePB.int_encoder(self.shareBuff, tag)
	if type(val) == "userdata" then
		local len = Lib.GetPBSize(val)
		OnePB.int_encoder(self.shareBuff, len)
		OnePB.blob_encoder(self.shareBuff, val)
	else
		local len = #val
		OnePB.int_encoder(self.shareBuff, len)
		OnePB.str_encoder(self.shareBuff, val)
	end

	-- local v = pb.int_encoder(tag)
	-- table.insert(self.fields, v)

	-- local len = #val
	-- local v = pb.int_encoder(len)
	-- table.insert(self.fields, v)
	-- table.insert(self.fields, val)
end

function WriteBuffer:WriteMsg(fieldId, val)
	local tag = fieldId * 8 + 2
	OnePB.int_encoder(self.shareBuff, tag)
	
	local curPos = OnePB.getCurSize(self.shareBuff)
	--先临时写入长度0 没有位置则后面追加 有位置则特定位置写入
	OnePB.fix32_encoder(self.shareBuff, 0)
	--加一个参数共享Buff Message 统一4字节长度
	val:Serialize(self.shareBuff)
	local newSize = OnePB.getCurSize(self.shareBuff)
	local msgLen = newSize-curPos-4
	OnePB.fix32_encoder(self.shareBuff, msgLen, curPos)

	-- local v = pb.int_encoder(tag)
	-- table.insert(self.fields, v)

	-- local buf = val:Serialize()
	-- local len = buf:GetSize()
	-- local v = pb.int_encoder(len)
	-- table.insert(self.fields, v)

	-- for k, v in ipairs(buf.fields) do
	-- 	table.insert(self.fields, v)
	-- end
end



function WriteBuffer:Finish( )
	-- return table.concat(self.fields)
	return self.shareBuff
end

function WriteBuffer:GetSize()
	local l = 0
	for k, v in ipairs(self.fields) do
		l = l + #v
	end
	return l
end
	

--将普通Table 转化为 ProtoBuf 对象
--可以序列化反序列化
function Lib.SetIndex(obj, cla)
	local meta = {
		__index = cla,
	}
	setmetatable(obj, meta)
end

function Lib.DumpPB(obj, key)
	Log("DumpPB", key, obj)
	for k, v in pairs(obj) do
		if type(v) == "table" then
			Lib.DumpPB(v, k)
		else
			Log(key, k, type(v), v)
		end
	end
end

--从字符串数据流 创建一个Buffer对象 用于解析
function Lib.CreateBuffer(st)
	local buffer = {}
	Lib.SetIndex(buffer, Buffer)
	buffer:Init(st)
	return buffer
end


function Lib.GetSerialize( tbData, protos )
	-- Log("GetSerialize", protos, debug.traceback())
	-- Lib:Tree(tbData)
	Lib.SetIndex(tbData, protos)
	local buf = tbData:Serialize()
	local ret = buf:Finish()
	-- OnePB.DumpBuff(ret)
	-- Log("Serialize", ret, Lib.GetPBSize(ret))
	return ret
end

function Lib.DeSerialize( tbData, protos )
	-- Log("DeSerialize", protos, debug.traceback())
	--单服模式下玩家数据是UserData 需要转化为String 来反序列化
	if type(tbData) == "userdata" then
		tbData = OnePB.getString(tbData)
	end
	-- Log("tbData", #tbData)
	-- OnePB.DumpBin(tbData)	
	local msg = {}
	Lib.SetIndex(msg, protos)
	msg:Parse(Lib.CreateBuffer(tbData))
	-- Lib:Tree(msg)
	return msg
end

function Lib.DefaultDeSerialize( tbData, protos )
	local msg = Lib.DeSerialize(tbData, protos)
	msg:SetDefault()
	return msg
end

function Lib.GetPBSize( shareBuff )
	return OnePB.getCurSize(shareBuff)
end